package com.safetys.co.gateway.service;

import cn.hutool.core.lang.UUID;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.*;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import cn.hutool.setting.Setting;
import com.safetys.co.apisix.entity.Route;
import com.safetys.co.apisix.entity.Upstream;
import com.safetys.co.apisix.ex.APISixException;
import com.safetys.co.apisix.plugin.ForwardAuth;
import com.safetys.co.apisix.plugin.ProxyRewrite;
import com.safetys.co.apisix.service.APISixPluginsService;
import com.safetys.co.apisix.service.APISixRoutesService;
import com.safetys.co.apisix.service.APISixUpstreamsService;
import com.safetys.co.gateway.constant.StrategyConstants;
import com.safetys.co.gateway.entity.Gateway;
import com.safetys.co.gateway.entity.Settings;
import lombok.ToString;
import lombok.extern.slf4j.Slf4j;

import java.time.Instant;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

/**
 * @author deqing.chedq
 * @Date 2025/5/17 18:08
 * @Description
 */
@Slf4j
@ToString
public class GatewayService {

    private static GatewayService gatewayService = null;
    private Settings settings = new Settings();

    private GatewayService() {

        super();
        Setting setting = new Setting("APISixHelper.properties");
        log.info(setting.toString());

        settings.setServiceURL(setting.get("apisix.serviceURL"));
        settings.setK(setting.get("apisix.k"));
    }

    private GatewayService(Settings settings) {

        super();
        this.settings = settings;
    }

    public Settings getSettings() {
        return settings;
    }

    public static GatewayService getInstance() {

        if (gatewayService == null) {
            HttpGlobalConfig.setTimeout(5000);
            gatewayService = new GatewayService();
        }
        return gatewayService;
    }

    public static GatewayService getInstance(Settings settings) {

        if (gatewayService == null) {
            HttpGlobalConfig.setTimeout(5000);
            gatewayService = new GatewayService(settings);
        }
        return gatewayService;
    }

    private String createPlugin(Gateway gateway) {

        APISixPluginsService apiSixPluginsService = APISixPluginsService.getInstance();
        JSONObject root = new JSONObject();

        ProxyRewrite proxyRewrite = new ProxyRewrite();
        List<String> regex_uri = new ArrayList<>();
        String proxy = String.format("^/%s/(.*)", gateway.getPrefix());
        regex_uri.add(proxy);
        regex_uri.add("/$1");
        proxyRewrite.setRegex_uri(regex_uri);
        JSONObject obj01 = JSONUtil.parseObj(proxyRewrite);

        ForwardAuth forwardAuth = new ForwardAuth();
        forwardAuth.setUri(gateway.getAuthURL());
        forwardAuth.getRequest_headers().addAll(gateway.getRequest_headers());
        forwardAuth.getUpstream_headers().addAll(gateway.getUpstream_headers());
        forwardAuth.getClient_headers().addAll(gateway.getClient_headers());
        JSONObject obj02 = JSONUtil.parseObj(forwardAuth);

        JSONObject objs = new JSONObject();
        objs.set("forward-auth", obj02);
        objs.set("proxy-rewrite", obj01);
        root.set("plugins", objs);

        String key = apiSixPluginsService.createResource(UUID.randomUUID().toString(), root);
        log.info(key);
        return key;
    }

    private String createUpstream(Gateway gateway) {

        APISixUpstreamsService apiSixUpstreamsService = APISixUpstreamsService.getInstance();
        Upstream upstream = new Upstream();
        String uuid = UUID.randomUUID().toString();
        upstream.setId(uuid);
        upstream.setName(uuid);
        upstream.setDesc(uuid);
        upstream.setType(gateway.getType());
        upstream.getNodes().putAll(gateway.getNodes());
        upstream.setRetries(3);
        upstream.setRetry_timeout(3000);
        String key = apiSixUpstreamsService.createResource(uuid, upstream);
        log.info(key);
        return key;
    }

    @Deprecated
    public com.safetys.co.gateway.entity.ret.Gateway create(Gateway gateway) throws APISixException {

        com.safetys.co.gateway.entity.ret.Gateway ret = new com.safetys.co.gateway.entity.ret.Gateway();

        String plugin_key = createPlugin(gateway);
        ret.setPlugin_config_id(plugin_key);

        String upstream_key = createUpstream(gateway);
        ret.setUpstream_id(upstream_key);

        APISixRoutesService apiSixRoutesService = APISixRoutesService.getInstance();
        Route route = new Route();
        route.setId(gateway.getId());
        route.setName(gateway.getName());
        route.setDesc(gateway.getDesc());
        String url = String.format("/%s/*", gateway.getPrefix());
        route.setUri(url);
        route.setPlugin_config_id(plugin_key);
        route.setUpstream_id(upstream_key);
        String key = apiSixRoutesService.createResource(route);
        log.info(key);
        ret.setRoute_id(key);
        return ret;
    }

    public Boolean enabled(String route_id, Byte enabled) {

        APISixRoutesService apiSixRoutesService = APISixRoutesService.getInstance();
        Route route = new Route();
        route.setStatus(enabled);
        try {
            JSONObject object = apiSixRoutesService.updateResource(route_id, route);
            log.info(object.toString());
        } catch (APISixException e) {
            return false;
        }
        return true;
    }

    public Boolean delete(String route_id) {

        APISixRoutesService apiSixRoutesService = APISixRoutesService.getInstance();
        APISixPluginsService apiSixPluginsService = APISixPluginsService.getInstance();
        APISixUpstreamsService apiSixUpstreamsService = APISixUpstreamsService.getInstance();

        try {
            JSONObject obj = apiSixRoutesService.getResource(route_id);
            log.info(obj.toString());
            JSONObject value = (JSONObject) obj.get("value");
            String plugin_config_id = (String) value.get("plugin_config_id");
            String upstream_id = (String) value.get("upstream_id");
            log.info(plugin_config_id);
            log.info(upstream_id);

            JSONObject plugin = apiSixPluginsService.getResource(plugin_config_id);
            JSONObject upstream = apiSixUpstreamsService.getResource(upstream_id);
            log.info(plugin.toString());
            log.info(upstream.toString());

            apiSixUpstreamsService.deleteResource(upstream_id);
            apiSixPluginsService.deleteResource(plugin_config_id);
            apiSixRoutesService.deleteResource(route_id);
        } catch (APISixException e) {
            return false;
        }
        return true;
    }

    @Deprecated
    public com.safetys.co.gateway.entity.ret.Gateway update(Gateway gateway) throws APISixException {

        APISixRoutesService apiSixRoutesService = APISixRoutesService.getInstance();
        APISixUpstreamsService apiSixUpstreamsService = APISixUpstreamsService.getInstance();
        APISixPluginsService apiSixPluginsService = APISixPluginsService.getInstance();

        com.safetys.co.gateway.entity.ret.Gateway ret = new com.safetys.co.gateway.entity.ret.Gateway();
        ret.setRoute_id(gateway.getId());
        JSONObject obj = apiSixRoutesService.getResource(gateway.getId());

        log.info(obj.toString());
        JSONObject value = (JSONObject) obj.get("value");
        String plugin_config_id = (String) value.get("plugin_config_id");
        String upstream_id = (String) value.get("upstream_id");
        log.info(plugin_config_id);
        log.info(upstream_id);
        ret.setPlugin_config_id(plugin_config_id);
        ret.setUpstream_id(upstream_id);

        JSONObject upstreamObj = (JSONObject) apiSixUpstreamsService.getResource(upstream_id).get("value");
        JSONObject pluginObj = (JSONObject) apiSixPluginsService.getResource(plugin_config_id).get("value");

        if (ObjectUtil.isNull(pluginObj)) {

            String plugin_key = createPlugin(gateway);
            ret.setPlugin_config_id(plugin_key);
            return ret;
        } else {
            // route Part
            Route route = new Route();
            if (StrUtil.isNotEmpty(gateway.getPrefix())) {
                String url = String.format("/%s/*", gateway.getPrefix());
                route.setUri(url);
            }
            if (StrUtil.isNotEmpty(gateway.getName())) {
                route.setName(gateway.getName());
            }
            if (StrUtil.isNotEmpty(gateway.getDesc())) {
                route.setDesc(gateway.getDesc());
            }
            apiSixRoutesService.updateResource(gateway.getId(), route);

            // upstream Part
            Upstream upstream = JSONUtil.toBean(upstreamObj, Upstream.class);
            if (StrUtil.isNotEmpty(upstream.getType())) {
                upstream.setType(StrategyConstants.EWMA);
            }
            if (gateway.getNodes().size() > 0) {
                upstream.getNodes().putAll(gateway.getNodes());
            }
            apiSixUpstreamsService.updateResource(upstream.getId(), upstream);

            // plugin Part
            // proxyRewrite plugin
            JSONObject proxyRewrite = (JSONObject) ((JSONObject) pluginObj.get("plugins")).get("proxy-rewrite");
            JSONArray regex_uri = (JSONArray) proxyRewrite.get("regex_uri");

            // No Need Get Old Proxy
            String proxy = regex_uri.get(0).toString();
            if (StrUtil.isNotEmpty(gateway.getPrefix())) {
                proxy = String.format("^/%s/(.*)", gateway.getPrefix());
                regex_uri.set(0, proxy);
            }
            log.info(proxyRewrite.toString());

            // forwardAuth plugin
            JSONObject forwardAuth = (JSONObject) ((JSONObject) pluginObj.get("plugins")).get("forward-auth");
            if (StrUtil.isNotEmpty(gateway.getAuthURL())) {
                forwardAuth.set("uri", gateway.getAuthURL());
            }
            if (gateway.getRequest_headers().size() > 0) {
                forwardAuth.set("request_headers", gateway.getRequest_headers());
            }
            if (gateway.getUpstream_headers().size() > 0) {
                forwardAuth.set("upstream_headers", gateway.getUpstream_headers());
            }
            if (gateway.getClient_headers().size() > 0) {
                forwardAuth.set("client_headers", gateway.getClient_headers());
            }
            log.info(forwardAuth.toString());
            apiSixPluginsService.createResource(plugin_config_id, pluginObj);
            return ret;
        }
    }
}
